//! 提供类似libc::getopt的功能.
//! 1. 单ascii字符的选项: `-a -b` 或者 `-ab`, `-c value` 或者 `-cvalue`
//! 2. 长字符串选项: `--long=value` 或者 `--long value`
//! 3. 选项结束标志: `--`
//! 4. 不限制非选项的输入顺序: `-c value arg` 或者 `arg -c value`
//!
//! 分别提供遍历选项和非选项的函数接口.
//!
//! ## Example
//! ```rust
//! use hiopt::{options, OptErr};
//! let opts = options!["a", "b:", "long:"];
//! let args = ["program", "-acbb-arg", "--long=long-arg", "--abc", "arg", "-b", "--", "-abc", "--long"];
//! let mut iter = opts.opt_iter(&args);
//! assert_eq!(iter.next(), Some(Ok((0, None)))); // opts[0].name = "a"
//! assert_eq!(iter.next(), Some(Err(OptErr::NoShortOpt(b'c'))));
//! assert_eq!(iter.next(), Some(Ok((1, Some("b-arg"))))); // opts[1].name = "b"
//! assert_eq!(iter.next(), Some(Ok((2, Some("long-arg"))))); // opts[2].name = "long"
//! assert_eq!(iter.next(), Some(Err(OptErr::NoLongOpt("abc"))));
//! assert_eq!(iter.next(), Some(Err(OptErr::NoArg(1)))); // opts[1].name = "b"
//! assert_eq!(iter.next(), None);
//!
//! let mut iter = opts.noopt_iter(&args);
//! assert_eq!(iter.next(), Some("program"));
//! assert_eq!(iter.next(), Some("arg"));
//! assert_eq!(iter.next(), Some("--"));
//! assert_eq!(iter.next(), Some("-abc"));
//! assert_eq!(iter.next(), Some("--long"));
//! assert_eq!(iter.next(), None);
//! ```
//!
//! 可以通过hictor crate获取应用程序的命令行输入进行分析处理
//!
//! ## Example
//! ```rust
//! let opts = hiopt::options!["a", "b:"];
//! let args = unsafe { hiopt::raw_args_from_i8(hictor::args()) };
//! for opt in opts.opt_iter(args) {
//!     println!("opt = {:?}", opt);
//! }
//!
//! for arg in opts.noopt_iter(args) {
//!     println!("arg = {:?}", arg);
//! }
//! ```
//!
//! 可以定义为静态变量或者常量.
//!
//! ## Example
//! ```rust
//! static OPTS: hiopt::Options<'static> = hiopt::options!["i", "input"];
//! 
//! fn parse_opts(opts: &hiopt::Options<'_>) {
//!     let args = unsafe { hiopt::raw_args_from_i8(hictor::args()) };
//!     for opt in opts.opt_iter(args) {
//!         println!("opt = {:?}", opt);
//!     }
//! }
//!
//! parse_opts(&OPTS);
//! ```
#![no_std]

use core::iter::Iterator;

/// 定义支持的所有选项数组
/// 1. 如果本选项要求携带参数，则应以`:`结束
///
/// ## Example
/// ```rust
/// let opts = hiopt::options!["a", "b:", "along", "blong:"];
/// //支持输入参数为 "-a -b b-arg --along --blong=blong-arg"
/// assert_eq!(opts[0].name(), "a");
/// assert_eq!(opts[1].name(), "b");
/// assert_eq!(opts[2].name(), "along");
/// assert_eq!(opts[3].name(), "blong");
/// ```
#[macro_export]
macro_rules! options {
    ($($name: expr), *) => {
        $crate::Options { opts: &[ $($crate::Opt::new($name)), *], }
    }
}

#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
pub struct Opt<'a> {
    name: &'a str,
    has_arg: bool,
}

impl<'a> Opt<'a> {
    /// 如果以':'结束，表示需要携带一个参数
    pub const fn new(name: &'a str) -> Self {
        if let Some(last) = name.as_bytes().last() {
            Self {
                name,
                has_arg: *last == b':',
            }
        } else {
            Self {
                name: "",
                has_arg: false,
            }
        }
    }

    pub fn name(&self) -> &str {
        if self.has_arg {
            return &self.name[0..self.name.len() - 1];
        }
        self.name
    }
    pub const fn has_arg(&self) -> bool {
        self.has_arg
    }
}

/// 定义期待的所有选项
pub struct Options<'a> {
    pub opts: &'a [Opt<'a>],
}

impl<'b> Options<'b> {
    fn find_by_id<'a>(&self, id: u8) -> Result<usize, OptErr<'a>> {
        let name = [id; 1];
        let name = unsafe { core::str::from_utf8_unchecked(&name) };
        for (idx, opt) in self.opts.iter().enumerate() {
            if opt.name() == name {
                return Ok(idx);
            }
        }
        Err(OptErr::NoShortOpt(id))
    }

    fn find_by_name<'a>(&self, name: &'a str) -> Result<usize, OptErr<'a>> {
        for (idx, opt) in self.opts.iter().enumerate() {
            if opt.name() == name {
                return Ok(idx);
            }
        }
        Err(OptErr::NoLongOpt(name))
    }

    pub fn get(&self, index: usize) -> Option<&Opt<'b>> {
        self.opts.get(index)
    }
}

impl<'a> core::ops::Index<usize> for Options<'a> {
    type Output = Opt<'a>;
    fn index(&self, index: usize) -> &Self::Output {
        &self.opts[index]
    }
}

#[derive(Clone, Debug)]
pub enum OptArg<'a> {
    Short(u8, &'a str),
    Long(&'a str, &'a str),
    Arg(&'a str),
}

/// 基于Arguments同时支持[*const u8]和[&str]两种类型, 前者对应获取的应用程序的输入
pub trait Arguments<'a> {
    /// 解析当前参数, 可能是Option也可能是Arg, 如果是"--"应该返回None
    fn try_opt(&self, idx: usize) -> Option<OptArg<'a>>;
    /// 获取当前option对应的输入参数，返回None没有找到对应的值，应该属于输入错误
    fn optarg(&self, idx: usize) -> Option<&'a str>;
    /// 无条件返回当前输入参数
    fn arg(&self, idx: usize) -> Option<&'a str>;
}

fn to_opt(bytes: &[u8]) -> Option<OptArg<'_>> {
    if bytes.is_empty() {
        None
    } else if bytes[0] != b'-' || bytes.len() == 1 {
        let s = unsafe { core::str::from_utf8_unchecked(bytes) };
        Some(OptArg::Arg(s))
    } else if bytes[1] != b'-' {
        let s = unsafe { core::str::from_utf8_unchecked(&bytes[2..]) };
        Some(OptArg::Short(bytes[1], s))
    } else if bytes.len() > 2 {
        let mut end = bytes.len();
        for (n, b) in bytes.iter().enumerate() {
            if *b == b'=' {
                end = n;
                break;
            }
        }
        let opt = unsafe { core::str::from_utf8_unchecked(&bytes[2..end]) };
        let val = if end < bytes.len() {
            unsafe { core::str::from_utf8_unchecked(&bytes[end + 1..]) }
        } else {
            ""
        };
        Some(OptArg::Long(opt, val))
    } else {
        None
    }
}

/// 统一按照`[u8]`进行选项匹配
pub trait AsBytes<'a> {
    fn as_bytes(&self) -> &'a [u8];
}

impl<'a> AsBytes<'a> for &'a str {
    fn as_bytes(&self) -> &'a [u8] {
        core::primitive::str::as_bytes(self)
    }
}

/// 封装C字符串
#[repr(transparent)]
pub struct RawArg(*const u8);

/// # Safety
/// raw需要是有效的C字符串数组
pub unsafe fn raw_args_from_i8(raw: &[*const i8]) -> &[RawArg] {
    core::mem::transmute::<_, &[RawArg]>(raw)
}

/// # Safety
/// raw需要是有效的C字符串数组
pub unsafe fn raw_args_from_u8(raw: &[*const u8]) -> &[RawArg] {
    core::mem::transmute::<_, &[RawArg]>(raw)
}

impl<'a> AsBytes<'a> for RawArg {
    fn as_bytes(&self) -> &'a [u8] {
        let mut len = 0;
        let mut p = self.0;
        unsafe {
            while *p != 0 {
                len += 1;
                p = p.add(1);
            }
            core::slice::from_raw_parts(self.0, len)
        }
    }
}

impl<'a, T: AsBytes<'a>> Arguments<'a> for [T] {
    fn try_opt(&self, idx: usize) -> Option<OptArg<'a>> {
        let arg = match self.get(idx) {
            Some(arg) => arg.as_bytes(),
            None => return None,
        };
        to_opt(arg)
    }

    fn optarg(&self, idx: usize) -> Option<&'a str> {
        let arg = match self.get(idx) {
            Some(arg) => arg.as_bytes(),
            None => return None,
        };
        if arg != b"--" {
            Some(unsafe { core::str::from_utf8_unchecked(arg) })
        } else {
            None
        }
    }

    fn arg(&self, idx: usize) -> Option<&'a str> {
        let arg = match self.get(idx) {
            Some(arg) => arg.as_bytes(),
            None => return None,
        };
        Some(unsafe { core::str::from_utf8_unchecked(arg) })
    }
}

impl<'a, T: AsBytes<'a>, const N: usize> Arguments<'a> for [T; N] {
    fn try_opt(&self, idx: usize) -> Option<OptArg<'a>> {
        Arguments::try_opt(&self[..], idx)
    }
    fn optarg(&self, idx: usize) -> Option<&'a str> {
        Arguments::optarg(&self[..], idx)
    }
    fn arg(&self, idx: usize) -> Option<&'a str> {
        Arguments::arg(&self[..], idx)
    }
}

#[derive(Debug, Eq, PartialEq, Ord, PartialOrd)]
pub enum OptErr<'a> {
    NoShortOpt(u8),
    NoLongOpt(&'a str),
    NoArg(usize),
}

pub struct OptIter<'a: 'b, 'b, Args: Arguments<'a> + ?Sized> {
    args: &'b Args,
    opts: &'b Options<'b>,
    opt: Option<OptArg<'a>>,
    idx: usize,
    opt_end: bool,
}

impl<'a: 'b, 'b, Args: Arguments<'a> + ?Sized> OptIter<'a, 'b, Args> {
    fn new(opts: &'b Options<'b>, args: &'b Args) -> Self {
        Self {
            opts,
            args,
            opt: None,
            idx: 0,
            opt_end: false,
        }
    }

    fn optarg(&mut self) -> Option<&'a str> {
        let arg = match self.opt {
            Some(OptArg::Short(_, arg)) => arg,
            Some(OptArg::Long(_, arg)) => arg,
            Some(OptArg::Arg(_)) => unreachable!(),
            None => "",
        };
        self.opt = None;
        if arg.is_empty() {
            self.idx += 1;
            match self.args.optarg(self.idx) {
                Some(arg) => {
                    self.idx += 1;
                    Some(arg)
                }
                None => None,
            }
        } else {
            self.idx += 1;
            Some(arg)
        }
    }

    fn opt(&mut self) -> Option<OptArg<'a>> {
        match self.opt {
            Some(OptArg::Short(_, arg)) => {
                if arg.is_empty() {
                    self.idx += 1;
                } else {
                    self.opt = Some(OptArg::Short(arg.as_bytes()[0], &arg[1..]));
                    return self.opt.clone();
                }
            }
            Some(_) => {
                self.idx += 1;
            }
            None => (),
        }
        self.opt = self.args.try_opt(self.idx);
        self.opt.clone()
    }

    fn next_opt(&mut self) -> Option<Result<(usize, Option<&'a str>), OptErr<'a>>> {
        loop {
            if self.opt_end {
                return None;
            }
            let idx = match self.opt() {
                Some(OptArg::Short(id, _)) => self.opts.find_by_id(id),
                Some(OptArg::Long(name, _)) => self.opts.find_by_name(name),
                Some(_) => continue,
                None => {
                    self.opt_end = true;
                    return None;
                }
            };
            match idx {
                Ok(idx) => {
                    let opt = &self.opts.opts[idx];
                    if opt.has_arg {
                        if let Some(arg) = self.optarg() {
                            return Some(Ok((idx, Some(arg))));
                        }
                        self.opt_end = true;
                        return Some(Err(OptErr::NoArg(idx)));
                    } else {
                        return Some(Ok((idx, None)));
                    }
                }
                Err(err) => return Some(Err(err)),
            }
        }
    }
}

impl<'a: 'b, 'b, Args: Arguments<'a> + ?Sized> Iterator for OptIter<'a, 'b, Args> {
    type Item = Result<(usize, Option<&'a str>), OptErr<'a>>;
    fn next(&mut self) -> Option<Self::Item> {
        self.next_opt()
    }
}

pub struct NooptIter<'a: 'b, 'b, Args: Arguments<'a> + ?Sized> {
    iter: OptIter<'a, 'b, Args>,
}

impl<'a: 'b, 'b, Args: Arguments<'a> + ?Sized> NooptIter<'a, 'b, Args> {
    fn new(opts: &'b Options<'b>, args: &'b Args) -> Self {
        Self {
            iter: OptIter::new(opts, args),
        }
    }

    fn next_arg(&mut self) -> Option<&'a str> {
        loop {
            if self.iter.opt_end {
                self.iter.idx += 1;
                return self.iter.args.arg(self.iter.idx - 1);
            }

            let idx = match self.iter.opt() {
                Some(OptArg::Short(id, _)) => self.iter.opts.find_by_id(id),
                Some(OptArg::Long(name, _)) => self.iter.opts.find_by_name(name),
                Some(OptArg::Arg(arg)) => return Some(arg),
                None => {
                    self.iter.opt_end = true;
                    continue;
                }
            };
            if let Ok(idx) = idx {
                let opt = &self.iter.opts.opts[idx];
                if opt.has_arg && self.iter.optarg().is_none() {
                    self.iter.opt_end = true;
                }
            }
        }
    }
}

impl<'a: 'b, 'b, Args: Arguments<'a> + ?Sized> Iterator for NooptIter<'a, 'b, Args> {
    type Item = &'a str;
    fn next(&mut self) -> Option<Self::Item> {
        self.next_arg()
    }
}

impl<'b> Options<'b> {
    /// 遍历所有的选项值
    pub fn opt_iter<'a, Args>(&'b self, args: &'b Args) -> OptIter<'a, 'b, Args>
    where
        Args: Arguments<'a> + ?Sized,
        'a: 'b,
    {
        OptIter::new(self, args)
    }

    /// 遍历所有的非选项值
    pub fn noopt_iter<'a, Args>(&'b self, args: &'b Args) -> NooptIter<'a, 'b, Args>
    where
        Args: Arguments<'a> + ?Sized,
        'a: 'b,
    {
        NooptIter::new(self, args)
    }
}

#[cfg(test)]
mod test {
    use super::{options, OptErr};
    #[test]
    fn test_short_0() {
        let opts = options!["a", "b:", "c", "d:"];
        let args = ["-a", "-b", "hello", "-c", "-d", "world", "end"];
        let mut iter = opts.opt_iter(&args);
        let opt = iter.next();
        assert_eq!(Some(Ok((0, None))), opt);
        let opt = iter.next();
        assert_eq!(Some(Ok((1, Some("hello")))), opt);
        let opt = iter.next();
        assert_eq!(Some(Ok((2, None))), opt);
        let opt = iter.next();
        assert_eq!(Some(Ok((3, Some("world")))), opt);
        let opt = iter.next();
        assert_eq!(opt, None);

        let mut iter = opts.noopt_iter(&args);
        let noopt = iter.next();
        assert_eq!(Some("end"), noopt);
        let noopt = iter.next();
        assert_eq!(noopt, None);
    }

    #[test]
    fn test_short_1() {
        let opts = options!["a", "b:", "c", "d:"];
        let args = ["-abhello", "-cdworld", "end"];
        let mut iter = opts.opt_iter(&args);
        let opt = iter.next();
        assert_eq!(Some(Ok((0, None))), opt);
        let opt = iter.next();
        assert_eq!(Some(Ok((1, Some("hello")))), opt);
        let opt = iter.next();
        assert_eq!(Some(Ok((2, None))), opt);
        let opt = iter.next();
        assert_eq!(Some(Ok((3, Some("world")))), opt);
        let opt = iter.next();
        assert_eq!(opt, None);

        let mut iter = opts.noopt_iter(&args);
        let noopt = iter.next();
        assert_eq!(Some("end"), noopt);
        let noopt = iter.next();
        assert_eq!(noopt, None);
    }

    #[test]
    fn test_short_2() {
        let opts = options!["a", "b:", "c", "d:"];
        let args = ["head", "-abhello", "middle", "-cdworld", "end"];
        let mut iter = opts.opt_iter(&args);
        let opt = iter.next();
        assert_eq!(Some(Ok((0, None))), opt);
        let opt = iter.next();
        assert_eq!(Some(Ok((1, Some("hello")))), opt);
        let opt = iter.next();
        assert_eq!(Some(Ok((2, None))), opt);
        let opt = iter.next();
        assert_eq!(Some(Ok((3, Some("world")))), opt);
        let opt = iter.next();
        assert_eq!(opt, None);

        let mut iter = opts.noopt_iter(&args);
        let noopt = iter.next();
        assert_eq!(Some("head"), noopt);
        let noopt = iter.next();
        assert_eq!(Some("middle"), noopt);
        let noopt = iter.next();
        assert_eq!(Some("end"), noopt);
        let noopt = iter.next();
        assert_eq!(noopt, None);
    }

    #[test]
    fn test_short_3() {
        let opts = options!["a", "b:", "c", "d:"];
        let args = ["-ahbhello", "-i", "-d", "--", "-cdworld", "end"];
        let mut iter = opts.opt_iter(&args);
        let opt = iter.next();
        assert_eq!(Some(Ok((0, None))), opt);
        let opt = iter.next();
        assert_eq!(Some(Err(OptErr::NoShortOpt(b'h'))), opt);
        let opt = iter.next();
        assert_eq!(Some(Ok((1, Some("hello")))), opt);
        let opt = iter.next();
        assert_eq!(Some(Err(OptErr::NoShortOpt(b'i'))), opt);
        let opt = iter.next();
        assert_eq!(Some(Err(OptErr::NoArg(3))), opt);
        let opt = iter.next();
        assert_eq!(opt, None);

        let mut iter = opts.noopt_iter(&args);
        let noopt = iter.next();
        assert_eq!(Some("--"), noopt);
        let noopt = iter.next();
        assert_eq!(Some("-cdworld"), noopt);
        let noopt = iter.next();
        assert_eq!(Some("end"), noopt);
        let noopt = iter.next();
        assert_eq!(noopt, None);
    }

    #[test]
    fn test_short_4() {
        let opts = options!["a", "b:", "c", "d:"];
        let args = [
            "head",
            "-acbhello",
            "middle",
            "-d",
            "world",
            "--",
            "-abhello",
            "end",
        ];
        let mut iter = opts.opt_iter(&args);
        let opt = iter.next();
        assert_eq!(Some(Ok((0, None))), opt);
        let opt = iter.next();
        assert_eq!(Some(Ok((2, None))), opt);
        let opt = iter.next();
        assert_eq!(Some(Ok((1, Some("hello")))), opt);
        let opt = iter.next();
        assert_eq!(Some(Ok((3, Some("world")))), opt);
        let opt = iter.next();
        assert_eq!(opt, None);

        let mut iter = opts.noopt_iter(&args);
        let noopt = iter.next();
        assert_eq!(Some("head"), noopt);
        let noopt = iter.next();
        assert_eq!(Some("middle"), noopt);
        let noopt = iter.next();
        assert_eq!(Some("--"), noopt);
        let noopt = iter.next();
        assert_eq!(Some("-abhello"), noopt);
        let noopt = iter.next();
        assert_eq!(Some("end"), noopt);
        let noopt = iter.next();
        assert_eq!(noopt, None);
    }

    #[test]
    fn test_long() {
        let opts = options!["ab", "cd:", "ef", "gh:"];
        let args = [
            "--ab",
            "--cd=hello",
            "--ef=ef",
            "--gh",
            "world",
            "--de",
            "--de=de",
            "--gh",
            "--",
            "end",
        ];
        let mut iter = opts.opt_iter(&args);
        let opt = iter.next();
        assert_eq!(Some(Ok((0, None))), opt);
        let opt = iter.next();
        assert_eq!(Some(Ok((1, Some("hello")))), opt);
        let opt = iter.next();
        assert_eq!(Some(Ok((2, None))), opt);
        let opt = iter.next();
        assert_eq!(Some(Ok((3, Some("world")))), opt);
        let opt = iter.next();
        assert_eq!(Some(Err(OptErr::NoLongOpt("de"))), opt);
        let opt = iter.next();
        assert_eq!(Some(Err(OptErr::NoLongOpt("de"))), opt);
        let opt = iter.next();
        assert_eq!(Some(Err(OptErr::NoArg(3))), opt);
        let opt = iter.next();
        assert_eq!(opt, None);

        let mut iter = opts.noopt_iter(&args);
        let noopt = iter.next();
        assert_eq!(Some("--"), noopt);
        let noopt = iter.next();
        assert_eq!(Some("end"), noopt);
        let noopt = iter.next();
        assert_eq!(noopt, None);
    }
}
